package pl.edu.pw.ee.buildcity.services;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import pl.edu.pw.ee.buildcity.dao.GenericDao;
import pl.edu.pw.ee.buildcity.domain.BuildStatus;
import pl.edu.pw.ee.buildcity.entities.BuildActivity;
import pl.edu.pw.ee.buildcity.entities.BuildDefinition;
import pl.edu.pw.ee.buildcity.entities.SCMCommit;
import pl.edu.pw.ee.buildcity.entities.SVNCommit;

import javax.annotation.Resource;
import java.util.*;

import static org.fest.assertions.Assertions.assertThat;

/**
 * @author mgrzechocinski
 * @since 2009-10-18
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:domain-context.xml", "classpath:core-context.xml", "classpath:test-context.xml"})
public class BuildActivityServiceIntegrationTestCase {

	@Resource(name = "buildActivityService")
	private BuildActivityService buildActivityService;

	@Resource(name = "jpaGenericDao")
	private GenericDao<BuildActivity, String> buildActivityDao;

	@Resource(name = "jpaGenericDao")
	private GenericDao<BuildDefinition, String> buildDefDao;

	@Test
	@Transactional
	public void shouldStoreBuildActivityStart() {
		// Given
		BuildDefinition bd = new BuildDefinition().withName("jobDefName");
		bd.setWorkingCopy(System.getProperty("user.dir"));
		bd.setRepositoryURL("http://example.com/trunk3");
		buildDefDao.persist(bd);

		// When
		buildActivityService.registerCodeChangedBuildActivityStart("jobDefName", null);

		// Then
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("name", "jobDefName");
		BuildActivity stored = buildActivityDao.getSingleResultOfNamedQuery("findAllBuildActivitiesOfBuildDefinition", params);
		assertThat(stored).isNotNull();
		assertThat(stored.getBuildDefinition()).isEqualTo(bd);
		assertThat(stored.getStarted()).isNotNull();
		assertThat(stored.getStarted().before(new Date())).isTrue();
		assertThat(stored.getEnded()).isNull();
		assertThat(stored.getResult()).isNotNull().isEqualTo(BuildStatus.IN_PROGRESS);
	}

	@Test
	@Transactional
	public void shouldStoreSucceededBuildActivity() {
		// Given
		BuildDefinition bd = new BuildDefinition().withName("jobDefName");
		bd.setWorkingCopy(System.getProperty("user.dir"));
		bd.setRepositoryURL("http://example.com/trunk3");
		buildDefDao.persist(bd);
		BuildActivity startedBuildActivity = buildActivityService.registerCodeChangedBuildActivityStart("jobDefName", null);

		// When
		buildActivityService.registerActivityFinish(startedBuildActivity, BuildStatus.SUCCEEDED);

		// Then
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("name", "jobDefName");
		BuildActivity stored = buildActivityDao.getSingleResultOfNamedQuery("findAllBuildActivitiesOfBuildDefinition", params);
		assertThat(stored).isNotNull();
		assertThat(stored.getResult()).isEqualTo(BuildStatus.SUCCEEDED);
	}

	@Test
	@Transactional
	public void shouldStoreFailedBuildActivity() {
		// Given
		BuildDefinition bd = new BuildDefinition().withName("jobDefName");
		bd.setWorkingCopy(System.getProperty("user.dir"));
		bd.setRepositoryURL("http://example.com/trunk3");
		buildDefDao.persist(bd);
		BuildActivity startedBuildActivity = buildActivityService.registerCodeChangedBuildActivityStart("jobDefName", null);

		// When
		buildActivityService.registerActivityFinish(startedBuildActivity, BuildStatus.FAILED);

		// Then
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("name", "jobDefName");
		BuildActivity stored = buildActivityDao.getSingleResultOfNamedQuery("findAllBuildActivitiesOfBuildDefinition", params);
		assertThat(stored).isNotNull();
		assertThat(stored.getResult()).isEqualTo(BuildStatus.FAILED);
	}


	@Test
	@Transactional
	public void shouldStoreAllAttributesOfBuildActivityWhenRegisteringItsFinished() throws InterruptedException {
		// Given
		BuildDefinition bd = new BuildDefinition().withName("jobDefName");
		bd.setWorkingCopy(System.getProperty("user.dir"));
		bd.setRepositoryURL("http://example.com/trunk3");
		buildDefDao.persist(bd);
		BuildActivity startedBuildActivity = buildActivityService.registerFreshBuildActivityStart("jobDefName");

		Date timestampBeforeFinished = new Date();
		Thread.sleep(1000); //to be sure that activity 'ended' is after now...

		// When
		buildActivityService.registerActivityFinish(startedBuildActivity, BuildStatus.SUCCEEDED);

		// Then

		Map<String, Object> params = new HashMap<String, Object>();
		params.put("name", "jobDefName");
		BuildActivity stored = buildActivityDao.getSingleResultOfNamedQuery("findAllBuildActivitiesOfBuildDefinition", params);
		assertThat(stored).isNotNull();
		assertThat(stored.getResult()).isEqualTo(BuildStatus.SUCCEEDED);
		assertThat(stored.getBuildNumber()).isEqualTo(1);
		assertThat(stored.getStarted()).isNotNull();
		assertThat(stored.getStarted().before(new Date())).isTrue();
		assertThat(stored.getEnded()).isNotNull();
		assertThat(stored.getEnded().after(timestampBeforeFinished)).isTrue();
		assertThat(stored.getBuildDefinition()).isNotNull();
		assertThat(stored.getBuildDefinition().getName()).isEqualTo("jobDefName");
		assertThat(stored.getResult()).isEqualTo(BuildStatus.SUCCEEDED);
	}

	@Test
	@Transactional
	public void shouldIncrementBuildNumberWhenRegisteringActivity() {
		// Given
		BuildDefinition bd = new BuildDefinition().withName("jobDefName");
		bd.setWorkingCopy(System.getProperty("user.dir"));
		bd.setRepositoryURL("http://example.com/trunk3");
		buildDefDao.persist(bd);

		BuildActivity firstActivity = buildActivityService.registerFreshBuildActivityStart("jobDefName");
		buildActivityService.registerActivityFinish(firstActivity, BuildStatus.SUCCEEDED);

		BuildActivity secondActivity = buildActivityService.registerFreshBuildActivityStart("jobDefName");

		// When
		buildActivityService.registerActivityFinish(secondActivity, BuildStatus.FAILED);

		// Then
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("name", "jobDefName");
		BuildActivity stored = buildActivityDao.executeNamedQuery("findAllBuildActivitiesOfBuildDefinition", params).get(0);
		assertThat(stored).isNotNull();
		assertThat(stored.getResult()).isEqualTo(BuildStatus.FAILED);
		assertThat(stored.getBuildNumber()).isEqualTo(2);
	}

	@Test
	@Transactional
	public void shouldSaveBuildActivityWithCascadeOnSCMCodeChange() {
		// Given
		BuildDefinition bd = new BuildDefinition().withName("jobDefName");
		bd.setWorkingCopy(System.getProperty("user.dir"));
		bd.setRepositoryURL("http://example.com/trunk3");
		buildDefDao.persist(bd);

		SCMCommit commit1 = new SVNCommit().onDate(new Date()).byAuthor("anyDeveloper1").withComment("some commnet1");
		SCMCommit commit2 = new SVNCommit().onDate(new Date()).byAuthor("anyDeveloper2").withComment("some commnet2");

		List<SCMCommit> commits = Arrays.asList(commit1, commit2);

		// When
		BuildActivity startedActivity = buildActivityService.registerCodeChangedBuildActivityStart("jobDefName", commits);

		// Then
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("name", "jobDefName");
		BuildActivity stored = buildActivityDao.executeNamedQuery("findAllBuildActivitiesOfBuildDefinition", params).get(0);
		assertThat(stored).isNotNull();
		assertThat(stored.getCommits()).hasSize(2);
	}
}
